#pragma once
#include <chrono>
#include <string>
#include <ctime>

namespace cpp
{
	static inline uint64_t getTimestamp()
	{
		return std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).
			   time_since_epoch().
			   count();
	}

	static uint64_t clockPt2Timestamp(const std::chrono::time_point<std::chrono::system_clock>& t)
	{
		return std::chrono::time_point_cast<std::chrono::milliseconds>(t).time_since_epoch().count();
	}

	static inline std::string timestamp2Str(uint64_t timestamp_ms = getTimestamp())
	{
		std::time_t t_sec = timestamp_ms / 1000;
		uint32_t t_ms = timestamp_ms % 1000;
		char p_char_array_at_least_23_char[50];
		struct tm* p_tm = localtime((const time_t*) &t_sec); // no need to free this ptr, don't worry
		sprintf(p_char_array_at_least_23_char, "%04d-%02d-%02d %02d:%02d:%02d.%03d", p_tm->tm_year + 1900,
				p_tm->tm_mon + 1,
				p_tm->tm_mday, p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec, t_ms);
		return p_char_array_at_least_23_char;
	}

	static inline std::string timestamp2StrForFile(uint64_t timestamp_ms = getTimestamp())
	{
		std::time_t t_sec = timestamp_ms / 1000;
		uint32_t t_ms = timestamp_ms % 1000;
		char p_char_array_at_least_23_char[50];
		struct tm* p_tm = localtime((const time_t*) &t_sec); // no need to free this ptr, don't worry
		sprintf(p_char_array_at_least_23_char, "%04d-%02d-%02d_%02d-%02d-%02d.%03d", p_tm->tm_year + 1900,
				p_tm->tm_mon + 1,
				p_tm->tm_mday, p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec, t_ms);
		return p_char_array_at_least_23_char;
	}

	static inline std::string genDateStrForFolder(uint64_t timestamp_ms = getTimestamp())
	{
		std::time_t t_sec = timestamp_ms / 1000;
		char p_char_array_at_least_10_char[32];
		struct tm* p_tm = localtime((const time_t*) &t_sec); // no need to free this ptr, don't worry
		sprintf(p_char_array_at_least_10_char, "%04d-%02d-%02d", p_tm->tm_year + 1900, p_tm->tm_mon + 1, p_tm->tm_mday);
		return p_char_array_at_least_10_char;
	}

	static bool checkIfLastTimeout(uint64_t& timestamp_last, uint32_t timeout_ms = 3000)
	{
		uint64_t timestamp = getTimestamp();
		if (timestamp < timestamp_last + timeout_ms)
		{
			return false;
		}
		timestamp_last = timestamp;
		return true;
	}

	// sampling
#define IF_LAST_TIMEOUT(timeout_ms) \
    thread_local uint64_t timestamp_last_enter = 0; \
    if (cpp_utl::checkIfLastTimeout(timestamp_last_enter, timeout_ms))

	static bool checkIfFutureReachOnce(uint64_t& timestamp_last, bool& flag_reached, uint32_t timeout_ms = 3000)
	{
		if (flag_reached) return false;
		uint64_t timestamp = getTimestamp();
		if (timestamp > timestamp_last + timeout_ms)
		{
			flag_reached = true;
			return true;
		}
		return false;
	}

	// only once
#define IF_FUTURE_REACH_ONCE(timeout_ms) \
    thread_local bool flag_reached = false; \
    thread_local uint64_t timestamp_last_enter = cpp_utl::getTimestamp(); \
    if (cpp_utl::checkIfFutureReachOnce(timestamp_last_enter, flag_reached, timeout_ms))
}
